﻿using NLog;
using ServiceStack.Redis;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;

namespace KillMvc.Controllers
{
    public class SaleController : ApiController
    {
        private Logger LOG = LogManager.GetCurrentClassLogger();
        private RedisRateLimiter redisRateLimiter = new RedisRateLimiter(3);

        [HttpGet]
        public string GetGood(string goodName)
        {

            if (!redisRateLimiter.CheckLimit())
            {
                return "Limit request";
            }
            return "Get > " + goodName;
            //return "Get > " + goodName + ", CheckKey=" + redisRateLimiter.CheckKey();
        }

        [HttpPost]
        public String Sale(string goodName)
        {
            var saleDao = new SaleDao();

            if (string.IsNullOrEmpty(goodName))
                return String.Empty;

            try
            {

                var saleInfo = saleDao.SaleGood(goodName);
                if (saleInfo == null)
                {
                    return "异常了";
                }

                return "商品: " + saleInfo.GoodNum + ", 销售数量: " + saleInfo.SaleNum;
            }
            catch (Exception e1)
            {
                return e1.Message;
            }
        }
    }

    public class RedisRateLimiter
    {
        private static Logger LOG = LogManager.GetLogger("redis-limiter");

        private static readonly string TIME_KEY = "TIME_KEY";
        private static readonly string COUNTER_KEY = "COUNTER_KEY";

        public int MaxLimitNum { get; set; }

        public RedisRateLimiter(int maxLimitNum)
        {
            this.MaxLimitNum = maxLimitNum;
        }

        public bool CheckLimit()
        {
            using (var redisClient = RedisUtil.GetClient())
            {
                if (!redisClient.ContainsKey(TIME_KEY))
                {
                    LOG.Debug("Key Exp");
                    redisClient.Set<int>(TIME_KEY, 0, TimeSpan.FromSeconds(1));
                    redisClient.Set<long>(COUNTER_KEY, 0, TimeSpan.FromSeconds(1));
                }

                if (redisClient.ContainsKey(TIME_KEY))
                {
                    var current = redisClient.IncrementValue(COUNTER_KEY);
                    if (current > MaxLimitNum)
                    {
                        LOG.Error("限流成功,当前值: {0}, 限制最大值：{1}", current, MaxLimitNum);
                        return false;
                    }

                }
            }
            return true;
        }
    }

    public class RedisUtil
    {
        private static string _redisServer = "10.78.57.85:6379";
        private static PooledRedisClientManager _redisManager;

        public string KeyPrefix { get; set; }

        static RedisUtil()
        {
            _redisManager = new PooledRedisClientManager(new string[] { _redisServer }, new string[] { _redisServer },
                new RedisClientManagerConfig()
                {
                    MaxReadPoolSize = 1000,
                    MaxWritePoolSize = 1000
                });
        }

        public static IRedisClient GetClient()
        {
            return _redisManager.GetClient();
        }

    }

    public class SaleService
    {

    }

    public class SaleDao
    {
        private Logger LOG = LogManager.GetCurrentClassLogger();

        public SaleInfo SaleGood(string goodName)
        {
            SaleInfo saleInfo = null;
            var connectionString = ConfigurationManager.ConnectionStrings["TestDb"].ConnectionString;
            using (var conn = new SqlConnection(connectionString))
            {
                conn.Open();
                var tx = conn.BeginTransaction();

                try
                {

                    var querySaleInfoSql = @"SELECT *
FROM    dbo.t_sale 
WHERE   good_name = @good_name";
//                    var querySaleInfoSqlWithUPDLock = @"SELECT *
//FROM    dbo.t_sale WITH(UPDLOCK)
//WHERE   good_name = @good_name";
                    var cmd = conn.CreateCommand();
                    cmd.CommandType = System.Data.CommandType.Text;
                    cmd.CommandText = querySaleInfoSql;
                    var p = new SqlParameter("@good_name", SqlDbType.NVarChar, 100);
                    p.Value = goodName;

                    cmd.Parameters.Add(p);
                    cmd.Transaction = tx;

                    using (var rdr = cmd.ExecuteReader())
                    {
                        if (rdr.Read())
                        {
                            saleInfo = new SaleInfo();
                            saleInfo.Id = rdr.GetInt32(0);
                            saleInfo.GoodName = rdr.GetString(1);
                            saleInfo.GoodNum = rdr.GetInt32(2);
                            saleInfo.SaleNum = rdr.GetInt32(3);
                            saleInfo.Version = rdr.GetInt32(4);
                        }
                        else
                        {
                            throw new Exception("找不到商品: " + goodName + ".");
                        }
                    }

                    //var saleNumObj = cmd.ExecuteScalar();
                    //if (saleNumObj == null)
                    //{
                    //    throw new Exception("找不到商品: " + goodName + ".");
                    //}



                    if (saleInfo.SaleNum >= saleInfo.GoodNum)
                    {
                        tx.Rollback();
                        throw new Exception("商品已售空.");
                    }

                    var updateSaleInfoSql = @"UPDATE  dbo.t_sale
        SET     sale_num = sale_num + 1, version = version + 1
        WHERE   good_name = @good_name AND version = @version";

                    var p2 = new SqlParameter("@version", SqlDbType.Int, 4);
                    p2.Value = saleInfo.Version;

                    cmd.Parameters.Add(p2);

                    cmd.CommandText = updateSaleInfoSql;
                    var rowCount = cmd.ExecuteNonQuery();
                    if (rowCount == 0)
                    {
                        var error = "并发更新异常, 更新行数: " + rowCount.ToString();
                        LOG.Error(error);
                        throw new Exception(error);
                    }

                    querySaleInfoSql = @"SELECT *
FROM    dbo.t_sale
WHERE   good_name = @good_name";

                    var insertLogSql = @"INSERT INTO dbo.t_sale_log
        ( good_name )
VALUES  ( @good_name)";
                    cmd.CommandText = insertLogSql;
                    cmd.ExecuteNonQuery();


                    cmd.CommandText = querySaleInfoSql;



                    using (var rdr = cmd.ExecuteReader())
                    {
                        if (rdr.Read())
                        {
                            saleInfo = new SaleInfo();
                            saleInfo.Id = rdr.GetInt32(0);
                            saleInfo.GoodName = rdr.GetString(1);
                            saleInfo.GoodNum = rdr.GetInt32(2);
                            saleInfo.SaleNum = rdr.GetInt32(3);
                        }
                    }

                    tx.Commit();

                    conn.Close();
                }
                catch (SqlException e2)
                {
                    tx.Rollback();
                    throw new Exception("事务异常." + e2.Message, e2);
                }
            }

            return saleInfo;
        }
    }

    public class SaleInfo
    {
        public int Id { get; set; }

        public string GoodName { get; set; }

        public int GoodNum { get; set; }

        public int SaleNum { get; set; }

        public int Version { get; set; }
    }
}
